package com.yiwyn.drooldemo.drools;

import com.yiwyn.drooldemo.drools.domain.User;
import lombok.extern.slf4j.Slf4j;
import org.drools.compiler.kie.builder.impl.KieRepositoryImpl;
import org.drools.core.event.DebugAgendaEventListener;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.command.Command;
import org.kie.api.event.rule.DebugRuleRuntimeEventListener;
import org.kie.api.runtime.ExecutionResults;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.StatelessKieSession;
import org.kie.internal.io.ResourceFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Slf4j
@RestController
public class DroolsController {

    private final Map<String, KieBase> kieBaseConcurrentHashMap = new ConcurrentHashMap<>();

    @GetMapping("/drools/{count}")
    public String drools(@PathVariable(name = "count", required = false) Integer count) throws InterruptedException {
        if (count == null) {
            count = 100;
        }
        CountDownLatch latch = new CountDownLatch(count);

        for (int i = 0; i < count; i++) {
            String id = i + "";
            String fileNo = "test" + i % 9 + ".drl";
            String fileName = "E:\\JavaProject\\spring-demo\\spring-drools-demo\\src\\main\\resources\\drools\\" + fileNo;

            new Thread(() -> {
                doRuleNew(fileName, fileNo, id);
                latch.countDown();
            }).start();
        }
        latch.await();

        return "Hello Drools";
    }


    @GetMapping("/clear")
    public String clear() throws NoSuchFieldException, IllegalAccessException {
        log.info("clear");
        kieBaseConcurrentHashMap.clear();
        return "clear success";
    }

    @GetMapping("/clearDroolsCache")
    public String clearDroolsCache() throws NoSuchFieldException, IllegalAccessException {
        log.info("clearDroolsCache");
        KieRepository repository = KieServices.Factory.get().getRepository();
        clearDroolsCache(repository);
        return "clear success";
    }


    private void clearDroolsCache(KieRepository repository) throws NoSuchFieldException, IllegalAccessException {
        // 获取 KieRepositoryImpl 的私有字段 kieModuleRepo
        Field kieModuleRepoField = KieRepositoryImpl.class.getDeclaredField("kieModuleRepo");
        kieModuleRepoField.setAccessible(true); // 设置可访问性
        Object kieModuleRepo = kieModuleRepoField.get(repository);

        /**
         * {@link org.drools.compiler.kie.builder.impl.KieRepositoryImpl.KieModuleRepo.kieModules}
         */
        // 获取 KieModuleRepo 的私有字段 kieModules
        Field kieModulesField = kieModuleRepo.getClass().getDeclaredField("kieModules");
        kieModulesField.setAccessible(true);
        Map<?, ?> kieModules = (Map<?, ?>) kieModulesField.get(kieModuleRepo);

        // 获取 KieModuleRepo 的私有字段 oldKieModules
        Field oldKieModulesField = kieModuleRepo.getClass().getDeclaredField("oldKieModules");
        oldKieModulesField.setAccessible(true);
        Map<?, ?> oldKieModules = (Map<?, ?>) oldKieModulesField.get(kieModuleRepo);

        // 清空 kieModules 和 oldKieModules
        log.info("kieModules:{} ", kieModules);
        log.info("oldKieModules: {}", oldKieModules);

        kieModules.clear();
        oldKieModules.clear();

    }


    private void doRuleNew(String ruleName, String fileName, String id) {

        try {
            KieServices kieServices = KieServices.Factory.get();


            StatelessKieSession kieSession = getKieContainer(ruleName, kieServices, fileName, id);

            log.info("StatelessKieSession获取成功:{}", kieSession);
            List<Command> cmds = new ArrayList<>();

            User user = new User();
            user.setId(Integer.parseInt(id));
            user.setSender("YBank");
            user.setAmount(100.50);
            user.setAge(25);
            user.setGender("male");
            user.setStatus("active");
            user.setBalance(500.75);

            Command isFocusCom = kieServices.getCommands().newAgendaGroupSetFocus("abc");
            cmds.add(isFocusCom);
            Command channelRulesFactCom = kieServices.getCommands().newInsert(user, "user");
            Command newFireAllRules = kieServices.getCommands().newFireAllRules("firedCount");
            cmds.add(channelRulesFactCom);
            cmds.add(newFireAllRules);

            assert kieSession != null;

            ExecutionResults execute = kieSession.execute(kieServices.getCommands().newBatchExecution(cmds));
            int count = (Integer) execute.getValue("firedCount");
            log.info("执行文件：{},文件名:{} ,命中数量:{} ,命中结果:{}", ruleName, fileName, count, user.getErrMsg());
            if (!fileName.contains(user.getErrMsg())) {
                log.error("======================匹配文件错误");
            }
        } catch (Exception e) {
            log.error("出现异常", e);
        }
    }


    private StatelessKieSession getKieContainerCache(String rulesPath, KieServices kieServices, String fileName, String id) {
        try {
            KieBase kieBase = kieBaseConcurrentHashMap.computeIfAbsent(fileName, new Function<String, KieBase>() {
                @Override
                public KieBase apply(String s) {
                    log.info("开始创建规则：{}", s);
                    KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
                    Path fpath = Paths.get(rulesPath);
                    File file = fpath.toFile();
                    kieFileSystem.write(ResourceFactory.newFileResource(file));

                    // 变更ReleaseId，使用业务参数绑定artifaceId，使缓存取得业务需要的
                    ReleaseId defaultReleaseId = kieServices.getRepository().getDefaultReleaseId();
                    ReleaseId releaseId = kieServices.newReleaseId(defaultReleaseId.getGroupId(),
                            id, defaultReleaseId.getVersion());
                    kieFileSystem.generateAndWritePomXML(releaseId);


                    KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem).buildAll();
                    Results results = kieBuilder.getResults();
                    if (results.hasMessages(Message.Level.ERROR)) {
                        log.error(results.getMessages().toString());
                        throw new IllegalStateException("### errors ###");
                    }
                    KieContainer kieContainer = kieServices.newKieContainer(releaseId);
                    return kieContainer.getKieBase();
                }
            });

            StatelessKieSession statelessKieSession = kieBase.newStatelessKieSession();
            statelessKieSession.addEventListener(new DebugAgendaEventListener());
            statelessKieSession.addEventListener(new DebugRuleRuntimeEventListener());
            return statelessKieSession;

        } catch (Exception e) {
            log.error("加载kieServices时异常：", e);
        }
        return null;
    }

    private StatelessKieSession getKieContainer(String rulesPath, KieServices kieServices, String fileName, String id) {
        try {
            KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
            Path fpath = Paths.get(rulesPath);
            File file = fpath.toFile();
            kieFileSystem.write(ResourceFactory.newFileResource(file));

            // // 变更ReleaseId方案
            // ReleaseId defaultReleaseId = kieServices.getRepository().getDefaultReleaseId();
            // ReleaseId releaseId = kieServices.newReleaseId(defaultReleaseId.getGroupId(),
            //         id, defaultReleaseId.getVersion());
            // kieFileSystem.generateAndWritePomXML(releaseId);
            // 该方案会导致 org.drools.compiler.kie.builder.impl.KieRepositoryImpl.KieModuleRepo.kieModules 快速膨胀。


            // 这一步会缓存 kModule信息
            KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem).buildAll();
            // 这一步后 KieServices.Factory.get().getRepository() => KieRepositoryImpl单例
            // org.drools.compiler.kie.builder.impl.KieRepositoryImpl.KieModuleRepo.kieModules 这个缓存中就有了DefaultReleaseId的缓存
            Results results = kieBuilder.getResults();
            if (results.hasMessages(Message.Level.ERROR)) {
                log.error(results.getMessages().toString());
                throw new IllegalStateException("### errors ###");
            }
            // 变更ReleaseId方案
            // KieContainer kieContainer = kieServices.newKieContainer(releaseId);

            // 到这一步获取 kieContainer，第一行代码  InternalKieModule kieModule = (InternalKieModule) getRepository().getKieModule(releaseId); 后面基本就不用看了
            KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
            // log.info("===========kieContainer:{}", kieContainer);
            KieBase kieBase = kieContainer.getKieBase();
            // log.info("===========kieBase:{}", kieBase);

            StatelessKieSession statelessKieSession = kieBase.newStatelessKieSession();
            statelessKieSession.addEventListener(new DebugAgendaEventListener());
            statelessKieSession.addEventListener(new DebugRuleRuntimeEventListener());
            return statelessKieSession;

        } catch (Exception e) {
            log.error("加载kieServices时异常：", e);
        }
        return null;
    }


}
